package com.ljp.makefriend.WebSocket;

import com.alibaba.fastjson.JSON;
import com.ljp.makefriend.WebSocket.pojo.ResultMessage;
import com.ljp.makefriend.common.ErrorCode;
import com.ljp.makefriend.config.GetHttpSessionConfig;
import com.ljp.makefriend.contant.UserConstant;
import com.ljp.makefriend.exception.BusinessException;
import com.ljp.makefriend.pojo.User;
import com.ljp.makefriend.service.ChatMessagesService;
import com.ljp.makefriend.service.impl.ChatMessagesServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket 配置端点类
 * @author 沐风
 */
@Component
@ServerEndpoint(value = "/chat/{receiverId}", configurator = GetHttpSessionConfig.class)
@Slf4j
public class ChatEndPoint {

    /**
     * 所有在线状态用户
     * 线程安全Set。用来存放每个用户对应的WebSocketServer对象。
     * 将用户 id 作为存储对象的键
     */
    private static final Map<Long, Session> onlineUsers = new ConcurrentHashMap<>();

    private static final List<Map<Long, Long>> chatIds = new ArrayList<>();

    // 聊天用户的 key
    public static HashSet<String> chatUserKeys = new HashSet<>();

    private static RedisTemplate<String, Object> redisTemplate;

    private HttpSession httpSession;


    @Autowired
    public void setYourService(RedisTemplate<String, Object> redisTemplate) {
        ChatEndPoint.redisTemplate = redisTemplate;
    }
    // 静态方法获取 Bean 的实例
    private static RedisTemplate<String, Object> getRedisTemplate() {
        if (redisTemplate == null) {
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(ChatEndPoint.class);
        }
        return redisTemplate;
    }

    private static ChatMessagesService chatMessagesService;

    @Autowired
    public void setChatMessagesService(ChatMessagesService chatMessagesService) {
        ChatEndPoint.chatMessagesService = chatMessagesService;
    }
    // 静态方法获取 Bean 的实例
    private static ChatMessagesService getChatMessagesService() {
        if (chatMessagesService == null) {
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(ChatEndPoint.class);
        }
        return chatMessagesService;
    }

    /**
     * 建立 WebSocket 连接成功后触发
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig,@PathParam("receiverId") String receiverId){
        // 1、存储会话信息（将 session 进行保存）
        this.httpSession = (HttpSession) endpointConfig.getUserProperties().get(HttpSession.class.getName());
        User user = (User) this.httpSession.getAttribute(UserConstant.USER_LOGIN_STATE);
            // 存储当前登录会话用户
        ChatMessagesServiceImpl.loginUser =user;
        long userId = user.getId();
        onlineUsers.put(userId, session);
        ChatMessagesService chatMessagesService = getChatMessagesService();
        // 获取聊天对象的 id
        long id = Long.parseLong(receiverId);
        System.out.println("-------建立连接成功-------");
        // 当用户建立连接的时候，标记对方发送的消息为已读
        boolean result = chatMessagesService.updateReadStatus(id, userId);
        if (result) {
            chatIds.clear();
        }
    }

    /**
     * 浏览器发送消息到服务端，该方法被调用
     * 张三 --> 李四
     * @param message 发送的消息
     */
    @OnMessage
    public void onMessage(String message) {
        try {
            // 将消息推送给指定的用户
            ResultMessage msg = JSON.parseObject(message, ResultMessage.class); // 将 JSON 转换成 java 对象
    //        System.out.println("msh:" + msg );
            // 获取接收者 id
            long receiverId = msg.getReceiverId();
            // 获取发送者 id
            long senderId = msg.getSenderId();

            // 记录当前聊天用户
            String key = "chat_message:sender_id:" + senderId + ":receiver_id:" + receiverId;
            chatUserKeys.add(key);

            // 存储当前聊天用户组，用于标识已读消息
            Map<Long, Long> chatId = new HashMap<>();
            chatId.put(senderId, receiverId);
            chatIds.add(chatId);

            // 创建一个新的 ResultMessage 对象，并将 msg 的属性复制给 resultMsg
            ResultMessage resultMsg = new ResultMessage();
            BeanUtils.copyProperties(msg, resultMsg);

            // 获取消息接收方对应的 session 对象
            Session receiverSession = onlineUsers.get(receiverId);
            Session senderSession = onlineUsers.get(senderId);
            boolean flg = true;
            // 构造发送消息
            if (receiverSession != null && senderSession != null) {
                // 当对方用户也在线的时候，匹配当前建立连接聊天的两个用户进行发送消息
                if (resultMsg.getSenderId() == senderId && resultMsg.getReceiverId() == receiverId){
                    String resultMessage = JSON.toJSONString(resultMsg);
                    // 通过 WebSocket 发送消息给接收者
                    receiverSession.getBasicRemote().sendText(resultMessage);
                    // 标记为已读
                    msg.setReadStatus(0);
                    flg = false;
                }
            }
            if (flg) {
                msg.setReadStatus(1);   // 标记为未读
            }

            // 将消息缓存进 Redis 中
            RedisTemplate<String, Object> redisTemplate = getRedisTemplate();
            redisTemplate.opsForList().rightPush(key, msg);

        } catch (Exception e) {
            log.info("发送消息错误：",e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,e.getMessage());
        }
    }

    /**
     * 断开连接时
     */
    @OnClose
    public void onClose(Session session) {
        // 1、从 onlineUsers 中剔除当前用户的 session 对象
        User user = (User) this.httpSession.getAttribute(UserConstant.USER_LOGIN_STATE);
        long userId = user.getId();
        onlineUsers.remove(userId);
        System.out.println("-------关闭连接成功-------");
    }

    /**
     * 发生错误时触发
     *
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        throwable.printStackTrace();
    }


}
